#pragma once
#include <random>
#include <string>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <fstream>

class Util {
public:
	static std::string intToString(int a) {
		std::stringstream ss;
		ss << a;
		return ss.str();
	}
	static std::string floatToString(float a) {
		std::stringstream ss;
		ss << a;
		return ss.str();
	}
	static std::string signIntToString(int a) {
		if(a >= 0)
			return "+" + intToString(a);
		return intToString(a);
	}
	static std::string boolToString(bool a) {
		if(a) return "true";
		return "false";
	}
	static bool an(char a) {
		switch(a) {
		case 'a':
		case 'e':
		case 'i':
		case 'o':
		case 'u':
			return true;
		default:
			return false;
		}
		return false;
	}
	static int letterMod(char c) {
		int mod = c;
		if(mod >= 'A' && mod <= 'Z')
			mod = mod-'A'+'z'+1-'a';
		else
			mod -= 'a';
		return mod;
	}
	static float normalize(float f) {
		return std::min(1.0f, std::max(0.0f, f));
	}

	static float normalize(float f, float min, float max) {
		return std::min(max, std::max(min, f));
	}

	static int* intSplit(std::string str, char delimiter = ' ') {
	    std::string item;
		int numChars = Util::numChars(str, delimiter), count = 0;
		int* tokens = new int[numChars+1];
		std::stringstream ss(str);
		while(std::getline(ss, item, delimiter)) {
			tokens[count++] = strToInt(item);
		}
		return tokens;
	}

	static int* intSplit(std::ifstream& file, char delimiter = ' ') {
		std::string line;
		std::getline(file, line);
		return intSplit(line, delimiter);
	}

	static float* floatSplit(std::string str, char delimiter = ' ') {
		std::string item;
		int numChars = Util::numChars(str, delimiter), count = 0;
		float* tokens = new float[numChars+1];
		std::stringstream ss(str);
		while(std::getline(ss, item, delimiter)) {
			tokens[count++] = strToFloat(item);
		}
		return tokens;
	}

	static float* floatSplit(std::ifstream& file, char delimiter = ' ') {
		std::string line;
		std::getline(file, line);
		return floatSplit(line, delimiter);
	}

	static int strToInt(std::string str) {
		return atoi(str.c_str());
	}

	static float strToFloat(std::string str) {
		return float(atof(str.c_str()));
	}

	static int numChars(std::string str, char c = ' ') {
		int chars = 0;
		for(int i = 0; i < int(str.size()); i++) {
			if(str.at(i) == c)
				chars++;
		}
		return chars;
	}

	// returns a list of points such that vec[0] is point[0].x and vec[1] is point[0].y
	// stolen from roguebasin
	static std::vector<int>* bresenham(int x1, int y1, int x2, int y2) {
		std::vector<int>* output = new std::vector<int>();
		int delta_x = x2 - x1;
		char ix = (delta_x > 0) - (delta_x < 0);
		delta_x = std::abs(delta_x) << 1;
 
		int delta_y(y2 - y1);
		char iy = (delta_y > 0) - (delta_y < 0);
		delta_y = std::abs(delta_y) << 1;
 
		output->push_back(x1);
		output->push_back(y1);
 
		if (delta_x >= delta_y) {
			// error may go below zero
			int error = delta_y - (delta_x >> 1);
			while (x1 != x2) {
				if (error >= 0) {
					if (error || (ix > 0)) {
						y1 += iy;
						error -= delta_x;
					}
				}
 
				x1 += ix;
				error += delta_y;
 
				output->push_back(x1);
				output->push_back(y1);
			}
		} else {
			// error may go below zero
			int error = delta_x - (delta_y >> 1);
			while (y1 != y2) {
				if (error >= 0) {
					if (error || (iy > 0)) {
						x1 += ix;
						error -= delta_y;
					}
				}
 
				y1 += iy;
				error += delta_x;
 
				output->push_back(x1);
				output->push_back(y1);
			}
		}
		return output;
	}
};

inline bool one_in(int x) { return rand()%x == 0;}
inline bool x_in_y(int x, int y) {return (rand()/double(RAND_MAX)) < (double(x)/double(y));}
inline int x_plus(int x, int y) {return x+(rand()%y);}
inline int x_plus_or_minus(int x, int y) {return x+(rand()%(y*2+1))-y;}
inline int x_between(int x, int y) {return (rand()%(y-x+1))+x;}
inline int rand(int x) {return rand()%x;}
inline int x_plus_one_rand(int x) {return rand()%(x+1);}
inline int rand_plus_one(int x) {return (rand()%x)+1;}
inline double distance(int x1, int y1, int x2, int y2) {return sqrt(pow(double(x2-x1),2)+pow(double(y2-y1),2));}
inline int distanceSquared(int x1, int y1, int x2, int y2) {return int(pow(double(x2-x1),2)+pow(double(y2-y1),2));}